package top.codedance.iotp.client.manage.net.com232;

import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.manage.ManageApplication;
import com.fazecast.jSerialComm.SerialPort;
import org.apache.log4j.Logger;

import javax.xml.bind.DatatypeConverter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class DownloadRunner implements Runner, Runnable {
    private Logger logger = ManageApplication.getLogger(DownloadRunner.class);
    private static ExecutorService pool = Executors.newCachedThreadPool();
    private static Map<Integer, DownloadRunner> tasks = new HashMap<>();
    private CountDownLatch latch = new CountDownLatch(0);
    private boolean isCancel = false;

    @Override
    public byte[] exec(byte[] data) {
        return new byte[0];
    }

    @Override
    public byte[] exec(byte[] data, final SerialPort serialPort) {

        try {
            String jsonStr = new String(data, StandardCharsets.UTF_8);
            JSONObject jsonObject = JSONObject.parseObject(jsonStr);
            int opration = jsonObject.getIntValue("opration");
            int taskId = jsonObject.getIntValue("taskId");
            if (opration == 0) {
                pool.submit(() -> {
                    try {
                        String source = jsonObject.getString("source");
                        File sourceFile = new File(source);
                        if (sourceFile.exists() && sourceFile.isFile()) {
                            long contentLength = sourceFile.length();
                            InputStream in = new FileInputStream(sourceFile);
                            int dataIndex = 0;
                            int readLength = -1;
                            byte[] tmp = new byte[1024 * 4];
                            long completeContentLength = 0;
                            while ((readLength = in.read(tmp)) != -1 && !isCancel) {
                                boolean await = latch.await(3, TimeUnit.SECONDS);
                                if (await) {
                                    latch = new CountDownLatch(1);
                                    byte[] filebody = Arrays.copyOf(tmp, readLength);
                                    JSONObject result = new JSONObject();
                                    result.put("ok", true);
                                    JSONObject context = new JSONObject();
                                    context.put("opration", 0);
                                    context.put("taskId", taskId);
                                    context.put("filename", sourceFile.getName().replaceAll("\\s+", ""));
                                    context.put("dataIndex", dataIndex);
                                    context.put("contentLength", contentLength);
                                    context.put("bodyLength", readLength);
                                    context.put("body", DatatypeConverter.printHexBinary(filebody));
                                    result.put("context", context);
                                    byte[] bytes = result.toJSONString().getBytes(StandardCharsets.UTF_8);
                                    serialPort.writeBytes(bytes, bytes.length);
                                    dataIndex++;
                                    completeContentLength += readLength;
                                    Double percentage = Math.floor(completeContentLength * 1.0 / contentLength * 100);
                                    logger.debug("dataIndex: " + dataIndex);
                                    logger.debug("Download complete :" + percentage + "%");
                                    if(completeContentLength >= contentLength){
                                        break;
                                    }
                                } else {
                                    latch.countDown();
                                    JSONObject result = new JSONObject();
                                    result.put("ok", false);
                                    JSONObject context = new JSONObject();
                                    context.put("opration", 0);
                                    context.put("msg", "Client no response.");
                                    result.put("context", context);
                                    byte[] bytes = result.toJSONString().getBytes(StandardCharsets.UTF_8);
                                    serialPort.writeBytes(bytes, bytes.length);
                                    break;
                                }
                            }
                            in.close();
                        } else {
                            JSONObject result = new JSONObject();
                            result.put("ok", false);
                            JSONObject context = new JSONObject();
                            context.put("opration", 0);
                            context.put("msg", "Not found file.");
                            result.put("context", context);
                            byte[] bytes = result.toJSONString().getBytes(StandardCharsets.UTF_8);
                            serialPort.writeBytes(bytes, bytes.length);
                        }
                    } catch (Exception e) {
                        JSONObject result = new JSONObject();
                        result.put("ok", false);
                        JSONObject context = new JSONObject();
                        context.put("opration", 0);
                        context.put("msg", e.getMessage());
                        result.put("context", context);
                        byte[] bytes = result.toJSONString().getBytes(StandardCharsets.UTF_8);
                        serialPort.writeBytes(bytes, bytes.length);
                    } finally {
                        tasks.remove(taskId);
                    }
                });
                tasks.put(taskId, this);
            }
            if (opration == 1) {
                if (tasks.containsKey(taskId)) {
                    DownloadRunner downloadRunner = tasks.get(taskId);
                    downloadRunner.cancel(taskId);
                }
                JSONObject result = new JSONObject();
                result.put("ok", true);
                JSONObject context = new JSONObject();
                context.put("opration", 1);
                context.put("taskId", taskId);
                result.put("context", context);
                byte[] bytes = result.toJSONString().getBytes(StandardCharsets.UTF_8);
                serialPort.writeBytes(bytes, bytes.length);
            }
            if (opration == 2) {
                if (tasks.containsKey(taskId)) {
                    DownloadRunner downloadRunner = tasks.get(taskId);
                    downloadRunner.ack();
                }
            }
        } catch (Exception e) {
            logger.debug(e.getMessage());
            JSONObject result = new JSONObject();
            result.put("ok", false);
            JSONObject context = new JSONObject();
            context.put("msg", e.getMessage());
            result.put("context", context);
            byte[] bytes = result.toJSONString().getBytes(StandardCharsets.UTF_8);
            serialPort.writeBytes(bytes, bytes.length);
        } finally {
            isCancel = false;
        }
        return new byte[0];
    }

    @Override
    public void run() {

    }

    /**
     * 取消任务
     */
    public void cancel(int taskId) {
        isCancel = true;
        try {
            Thread.sleep(500);
        } catch (Exception e) {
        }
        tasks.remove(taskId);
    }

    /**
     * 确认已收到，发送下一个包
     */
    public void ack() {
        latch.countDown();
    }
}
